import G6, { IG6GraphEvent, TreeGraph } from '@antv/g6'
import g6_reduce from './img/g6_reduce.png'
import g6_add from './img/g6_add.png'

function handleDepth0RectWidth(label: string) {
  return 80 + 20 * label.length
}

function handleNodeClick(event: IG6GraphEvent) {
  const item = event.item
  // 聚焦当前点击的节点（把节点放到视口中心）

  const matrix = item?.get('group').getMatrix()
  const point = {
    x: matrix[6],
    y: matrix[7]
  }
  const width = graph.get('width')
  const height = graph.get('height')
  // 找到视口中心
  const viewCenter = {
    x: width / 2,
    y: height / 2
  }
  const modelCenter = graph.getPointByCanvas(viewCenter.x, viewCenter.y)
  const viewportMatrix = graph.get('group').getMatrix()
  // 画布平移的目标位置，最终目标是graph.translate(dx, dy);
  const dx = (modelCenter.x - point.x) * viewportMatrix[0]
  const dy = (modelCenter.y - point.y) * viewportMatrix[4]
  let lastX = 0
  let lastY = 0
  let newX: any = void 0
  let newY: any = void 0
  // 动画每次平移一点，直到目标位置
  graph.get('canvas').animate(
    {
      onFrame: function onFrame(ratio: number) {
        newX = dx * ratio
        newY = dy * ratio
        graph.translate(newX - lastX, newY - lastY)
        lastX = newX
        lastY = newY
      }
    },
    300,
    'easeCubic'
  )
}

function handleDepth1RectWidth(label: string) {
  return 30 + 20 * label.length
}

function handleRectWidth(label: string, size: number) {
  let charLength = 0
  for (let i = 0; i < label.length; i++) {
    if (/[\x20-\xff]+/.test(label[i])) {
      charLength += size * 0.7
    } else {
      if (label[i] === '、') charLength += size + 6
      else charLength += size
    }
  }

  const width = charLength + 20
  const xNum = charLength + 16
  return {
    width,
    xNum
  }
}

/**
 * @description: 处理数据
 * @param {any} originData
 * @return {*}
 */
function handleData(originData: any) {
  const recursive = (item: any, parent: any) => {
    item.depth = parent.depth + 1
    item.xNum = 75
    if (item.depth > 1) {
      item.collapsed = true
    } else {
      item.collapsed = false
    }
    if (item.depth === 1) {
      item.width = handleDepth1RectWidth(item.label)
      item.xNum = handleRectWidth(item.label, 20).xNum
    } else if (item.depth > 1) {
      const addNum = 16
      item.width = handleRectWidth(item.label, addNum).width
      if (item.children && item.children.length) {
        item.width += 24
      }
      item.xNum = handleRectWidth(item.label, addNum).xNum
    }
    if (item.children && item.children.length) {
      item.children.forEach((ele: any) => {
        recursive(ele, item)
      })
    }
  }
  originData.depth = 0
  originData.width = 148
  if (originData.children) {
    originData.children.forEach((first: any) => {
      recursive(first, originData)
    })
  }
}

const getGraphDataIds = (originData: any, collapsed: boolean, depth = 2) => {
  const ids: string[] = []
  const recursive = (item: any) => {
    if (item.depth > 1) {
      item.collapsed = collapsed
    } else {
      item.collapsed = false
    }
    if (item.depth === depth && item.children && item.children.length > 0) {
      ids.push(item.id)
    }
    if (item.children && item.children.length) {
      item.children.forEach((ele: any) => {
        recursive(ele)
      })
    }
  }
  if (originData.children) {
    originData.children.forEach((first: any) => {
      recursive(first)
    })
  }
  return ids
}

let graph: TreeGraph
export default function useIndented(dom: string, originData: any, callBack: (nodeId: string | null, item?: any) => void) {
  const domEle = document.getElementById(dom)
  if (!domEle) return
  let prevId = ''
  handleData(originData)
  if (graph) {
    graph.destroy()
  }
  //  组件props
  const props = {
    data: originData,
    config: {
      padding: [20, 0],
      defaultLevel: 3,
      defaultZoom: 1
    }
  }
  // 默认配置
  const defaultConfig = {
    modes: {
      default: ['drag-canvas', 'zoom-canvas']
    },
    fitView: true,
    animate: true,
    defaultNode: {
      type: 'flow-rect'
    },
    defaultEdge: {
      type: 'cubic-horizontal',
      style: {
        stroke: '#CED4D9'
      }
    },
    layout: {
      type: 'mindmap',
      direction: 'H',
      dropCap: false,
      getHeight: () => {
        return 52
      },
      getHGap: (d: any) => {
        const { depth } = d
        if (depth > 1) {
          return 80
        } else {
          return 60
        }
      },
      getVGap: () => {
        return 1
      },
      getSide: (d: any) => {
        if (d.data.direction === 'left') {
          return 'left'
        }
        if (d.data.direction === 'top') {
          return 'top'
        }
        return 'right'
      }
    }
  }
  // 自定义节点、边
  const registerFn = () => {
    /**
     * 自定义节点
     */
    G6.registerNode(
      'flow-rect',
      {
        shapeType: 'flow-rect',
        draw(cfg: any, group: any) {
          const { label, width, xNum = 0, depth, children, collapsed } = cfg || {}
          // 逻辑不应该在这里判断
          const rectConfig = {
            width,
            height: 40,
            fontSize: 12,
            radius: 8,
            opacity: 1,
            cursor: 'pointer'
          }
          const nodeOrigin = {
            x: -rectConfig.width / 2,
            y: -rectConfig.height / 2
          }
          const textConfig = {
            textAlign: 'left',
            textBaseline: 'bottom'
          }
          const rect = group.addShape('rect', {
            attrs: {
              x: nodeOrigin.x,
              y: nodeOrigin.y,
              ...rectConfig
            }
          })
          const rectBg = 'rgba(29,133,255,0.1)'
          if (depth === 0) {
            // 根节点
            // 背景矩形
            const Basewidth = handleDepth0RectWidth(label)
            group.addShape('rect', {
              attrs: {
                x: nodeOrigin.x,
                y: nodeOrigin.y,
                width: Basewidth,
                height: 48,
                radius: 12,
                fill: rectBg,
                cursor: 'pointer'
              },
              name: 'root-rect'
            })

            group.addShape('text', {
              attrs: {
                ...textConfig,
                width: Basewidth,
                height: 48,
                x: Basewidth / 2 + nodeOrigin.x,
                y: 40 + nodeOrigin.y,
                text: label,
                fontSize: 28,
                fontWeight: 'bold',
                textAlign: 'center',
                lineHeight: 48,
                fill: '#1D85FF'
              },
              name: 'root-text'
            })
          } else if (depth === 1) {
            // 2级
            // 背景矩形
            group.addShape('rect', {
              attrs: {
                x: nodeOrigin.x + 10,
                y: nodeOrigin.y,
                width: handleDepth1RectWidth(label),
                height: 48,
                radius: 8,
                fill: rectBg,
                cursor: 'pointer'
              },
              name: 'second-rect'
            })
            // 名称
            group.addShape('text', {
              attrs: {
                ...textConfig,
                width: handleDepth1RectWidth(label),
                x: 24 + nodeOrigin.x,
                y: 34 + nodeOrigin.y,
                text: label,
                fontSize: 20,
                fontWeight: 'bold',
                fill: '#1D85FF'
              },
              name: 'second-text'
            })
          } else {
            const textFill = '#1D85FF'
            // 背景矩形
            group.addShape('rect', {
              attrs: {
                ...rectConfig,
                x: nodeOrigin.x,
                y: nodeOrigin.y,
                radius: 8,
                cursor: 'pointer',
                fill: rectBg
              },
              name: `children-rect`
            })
            // 名称
            group.addShape('text', {
              attrs: {
                ...textConfig,
                x: 12 + nodeOrigin.x,
                y: 28 + nodeOrigin.y,
                text: label,
                fontSize: 16,
                fontWeight: 'normal',
                fill: textFill,
                cursor: 'pointer'
              },
              name: `children-text`
            })
            if (children && children.length > 0) {
              group.addShape('image', {
                attrs: {
                  x: xNum + nodeOrigin.x,
                  y: 9 + nodeOrigin.y,
                  width: 21,
                  height: 21,
                  radius: 8,
                  cursor: 'pointer',
                  img: collapsed ? g6_add : g6_reduce
                },
                name: 'collapse-img',
                modelId: cfg?.id
              })
            }
          }

          return rect
        },

        setState(name, value, item) {
          if (name === 'collapse') {
            const group: any = item?.getContainer()
            const collapseImg = group.find((e: any) => e.get('name') === 'collapse-img')
            if (collapseImg) {
              if (!value) {
                collapseImg.attr({
                  img: g6_reduce
                })
              } else {
                collapseImg.attr({
                  img: g6_add
                })
              }
            }
          }
          if (name === 'active') {
            const group: any = item?.getContainer()
            const rectModel = group.find(
              (e: any) => e.get('name') === 'children-rect' || e.get('name') === 'second-rect' || e.get('name') === 'root-rect'
            )
            if (rectModel) {
              if (!value) {
                rectModel.attr({
                  stroke: 'transparent'
                })
              } else {
                rectModel.attr({
                  stroke: '#00bbff'
                })
              }
            }
            const textModel = group.find(
              (e: any) => e.get('name') === 'children-text' || e.get('name') === 'second-text' || e.get('name') === 'root-text'
            )
            if (textModel) {
              if (!value) {
                textModel.attr({
                  fill: '#1D85FF'
                })
              } else {
                textModel.attr({
                  fill: '#00bbff'
                })
              }
            }
          }
        }
      },
      'rect'
    )
  }
  registerFn()

  const getUpdateById = (id: string, type = '') => {
    const item = graph.findById(id)
    const nodeModel = item.getModel()
    if (!type) {
      nodeModel.collapsed = !nodeModel.collapsed
      graph.layout()
    }
    graph.setItemState(item, 'collapse', <boolean>nodeModel.collapsed)
  }
  const handleCollapse = (e: any) => {
    const target = e.target
    const id = target.get('modelId')
    getUpdateById(id)
  }

  const handleCancelAll = () => {
    if (prevId) {
      const prevItem = graph.findById(prevId)
      graph.setItemState(prevItem, 'active', false)
    }
  }
  const handleAllOpen = (collapsed: boolean) => {
    const ids = getGraphDataIds(originData, collapsed)
    graph.data(originData)
    graph.layout()
    ids.forEach((id) => {
      getUpdateById(id, 'all')
    })
  }
  const refresh = () => {
    graph.render()
  }
  const clear = () => {
    graph.destroy()
  }
  const changeData = (originDataParams: any) => {
    graph.changeData(originDataParams)
  }
  const { data } = props
  const initGraph = (data: any) => {
    const { config } = props
    graph = new G6.TreeGraph({
      container: dom,
      width: domEle.clientWidth,
      height: domEle.clientHeight,
      ...defaultConfig,
      ...config
    })

    graph.data(data)
    graph.render()
    // 获取三级节点总数量，数量太少缩小视图
    let nodeCount = 0
    data.children.forEach((second: { children: string | any[] }) => {
      nodeCount += second.children.length
    })
    if (nodeCount <= 10) {
      graph.fitView(180)
    } else {
      graph.fitView(30)
    }
    graph.on('node:click', (e) => {
      handleNodeClick(e)
      // const model: any = e.item?._cfg?.model
      // if (model.depth === 1) return
      const id: any = e.item?._cfg?.id
      // 上一个
      if (prevId && id !== prevId) {
        const prevItem = graph.findById(prevId)
        if (prevItem) graph.setItemState(prevItem, 'active', false)
        else prevId = ''
      }
      // 当前
      const states: any = e.item?._cfg?.states
      const item = graph.findById(id)
      if (!states.includes('active')) {
        callBack(id, e.item?._cfg)
        graph.setItemState(item, 'active', true)
      } else {
        callBack(null)
        graph.setItemState(item, 'active', false)
      }
      prevId = id
    })
    graph.on('collapse-img:click', (e) => {
      handleNodeClick(e)
      const prevItem = graph.findById(prevId)
      setTimeout(() => {
        if (!prevItem || prevItem.destroyed) {
          prevId = ''
        }
      }, 500)
      // 阻止冒泡
      e.bubbles = false
      handleCollapse(e)
    })
  }
  const activeTheNode = (nodeName: string) => {
    const allNodes: any = graph.getNodes()
    const nodeInfos = allNodes.map((node: any) => node._cfg?.model)
    let theIndex = -1
    nodeInfos.forEach((info: any, index: number) => {
      if (info.label === nodeName) {
        theIndex = index
      }
    })

    const id = allNodes[theIndex]._cfg.id
    // 上一个
    if (prevId && id !== prevId) {
      const prevItem = graph.findById(prevId)
      graph.setItemState(prevItem, 'active', false)
    }
    // 当前
    const item = allNodes[theIndex]
    const states: any = item?._cfg?.states

    if (!states.includes('active')) {
      callBack(id, item?._cfg)
      graph.setItemState(item, 'active', true)
    } else {
      callBack(null)
      graph.setItemState(item, 'active', false)
    }

    prevId = id ?? ''
  }

  const activeRootNode = () => {
    activeTheNode(originData.label)
  }

  initGraph(data)
  activeRootNode()
  return {
    handleAllOpen,
    handleCancelAll,
    changeData,
    refresh,
    activeTheNode,
    clear
  }
}
